Skip to content

Conversation

@Vodiboi
Copy link

@Vodiboi Vodiboi commented Nov 2, 2025

Now looks at the first 300 chars of text from a page and makes a space using it. Removed domain stuff and duplicating entries.

…or this (tabs, history, activeTab). Added chrome.png. Added the connection to connection_manager. Added background script in background.ts to get all tabs in the browser. This connection also has 2 error handlers (not enough tabs and no tabs found) that more-readably show to the user when either is the case.
…b contents for better space creation. Added scripting permission to package.json to allow for tab reading to occur.
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Vodiboi, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a major new capability to integrate and analyze a user's open Chrome browser tabs. It enables the application to gather information from active web pages, process their content, and present it in a structured 'space'. The changes encompass significant additions to the background script for tab and content handling, updates to extension permissions, and a new UI component for user interaction, alongside general improvements to data processing and error resilience.

Highlights

  • New Feature: Chrome Tabs Integration: A new connection type, 'Chrome Tabs', has been introduced, allowing the application to analyze all currently open browser tabs and their content to create a 'space'.
  • Advanced Page Content Extraction: The system now includes sophisticated logic to extract the first 300 characters of visible text from web pages. This involves prioritizing innerText and falling back to a TreeWalker for comprehensive text collection, while filtering out non-visible elements like scripts and styles.
  • Updated Chrome Extension Permissions: The package.json manifest has been updated to include new permissions (tabs, history, activeTab, scripting) essential for accessing and interacting with browser tabs and executing content scripts.
  • Centralized UUID Generation: The direct uuid dependency has been removed, and existing connections (like LinkedIn) now utilize a new getUuidV4() utility function, promoting consistency and potentially reducing bundle size.
  • Robust Space Creation with Retry Logic: A createSpaceWithAutoRetry function has been implemented to enhance the reliability of space creation, automatically retrying requests that encounter transient errors such as timeouts or gateway issues.
  • User Feedback for Tab Analysis: Custom error messages are now displayed to the user for scenarios where insufficient tabs are open or the extracted dataset is too small for meaningful space creation. A new UI element is also injected into Google search results to toggle the display of the tabs analysis.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new 'Tabs' feature, which creates a Mantis space based on the content of currently open browser tabs. The implementation includes a new background script logic to fetch tab content, a new connection ChromeTabsConnection, and UI integration on the Google search page. Additionally, it refactors UUID generation by removing the uuid package in favor of a centralized helper.

My review focuses on a few key areas. There's a critical issue in src/background.ts where multiple onMessage listeners are used, which should be consolidated into one to prevent unpredictable behavior. I've also pointed out some opportunities to improve code quality in the new chromeTabs connection by using specific types instead of any and adhering to best practices for event handling. Using console.error for logging errors is also recommended for better diagnostics.

Comment on lines 2 to 57
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
// Handle getTabs request for Chrome Tabs connection
if (request.action === "getTabs") {
chrome.tabs.query({}, (tabs) => {
if (chrome.runtime.lastError) {
sendResponse({ error: chrome.runtime.lastError.message });
} else {
sendResponse({ tabs: tabs });
}
});
return true;
}

// Handle getTabsWithContent request
if (request.action === "getTabsWithContent") {
chrome.tabs.query({}, async (tabs) => {
if (chrome.runtime.lastError) {
sendResponse({ error: chrome.runtime.lastError.message });
return;
}

const tabsWithContent = [];

for (const tab of tabs) {
const tabData = { ...tab, pageContent: '' }; // Add pageContent property

// Try to get page content for each tab
try {
if (tab.id && tab.url && !tab.url.startsWith('chrome://') && !tab.url.startsWith('chrome-extension://')) {
// Execute content script to get page text
const results = await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: getPageContent, // Use 'func' instead of 'function'
});

if (results && results[0] && results[0].result) {
tabData.pageContent = results[0].result;
}
}
} catch (error) {
console.log(`Could not get content for tab ${tab.id}:`, error);
// Set a fallback description
tabData.pageContent = `Content from ${tab.url ? new URL(tab.url).hostname : 'unknown site'} - unable to read page content`;
}

tabsWithContent.push(tabData);
}

sendResponse({ tabs: tabsWithContent });
});
return true;
}

// Don't interfere with other message handlers
return false;
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

You've added a new chrome.runtime.onMessage.addListener. However, this file now contains multiple onMessage listeners (e.g., another one starts on line 169, and more follow). Registering multiple listeners for the onMessage event is an anti-pattern in Chrome extension development and can lead to unpredictable behavior, race conditions, or messages not being handled as expected. Only one listener should be active for the runtime.onMessage event.

It's highly recommended to consolidate all message handling logic into a single chrome.runtime.onMessage.addListener block, using if/else if or a switch statement to delegate based on request.action.

Example of consolidation:

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    switch (request.action) {
        case "getTabs":
            // ... logic for getTabs
            return true; // Indicate async response
        case "getTabsWithContent":
            // ... logic for getTabsWithContent
            return true; // Indicate async response
        case "setCookie":
            // ... logic for setCookie
            return true; // Indicate async response
        // ... other cases
        default:
            // Optional: handle unknown actions
            break;
    }
    return false;
});

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this something that should be fixed Luca? This problem predates my tabs feature, so I assume you did this intentionally...

Comment on lines +167 to +174
<button onclick="this.parentElement.remove()" style="
background: rgba(255, 255, 255, 0.2);
border: 1px solid rgba(255, 255, 255, 0.3);
color: white;
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
">Got it</button>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using an inline onclick attribute is generally discouraged. It's better practice to attach event listeners programmatically using addEventListener. This improves separation of concerns (HTML structure vs. JS behavior) and makes the code more maintainable. A possible implementation would be to add an ID to the button, append the errorDiv to the body, then find the button by its ID and add the click listener. This also applies to the showNoTabsError function.

Comment on lines +204 to +211
<button onclick="this.parentElement.remove()" style="
background: rgba(255, 255, 255, 0.2);
border: 1px solid rgba(255, 255, 255, 0.3);
color: white;
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
">OK</button>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to the showDatasetTooSmallError function, using an inline onclick attribute is discouraged. It's better practice to attach event listeners programmatically using addEventListener. This improves code maintainability and separation of concerns.

Vodiboi and others added 3 commits November 2, 2025 12:11
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
type fix

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@Vodiboi
Copy link
Author

Vodiboi commented Nov 10, 2025

@gemini-code-assist

@gemini-code-assist
Copy link
Contributor

Hello @Vodiboi! I'm here to help. Do you have any questions about the summary I provided, or anything else regarding this pull request?

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new 'Tabs' feature that creates a Mantis space from the content of all open browser tabs. The implementation includes adding a new Chrome Tabs connection, background script logic to fetch tab content, and the necessary permissions. The pull request also includes a good refactoring to replace the uuid package with the native crypto.randomUUID().

My review focuses on a critical issue in the new background script message listener that could break existing functionality. I've also provided suggestions to improve error messaging for a better user experience and to enhance type safety in the new connection for improved code quality and maintainability.

Comment on lines +55 to +56
// Don't interfere with other message handlers
return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This return false is incorrect and can break other message listeners. In a Chrome extension's onMessage listener, you should only return true if you are sending a response asynchronously. If a message is not handled by this listener, it should have no explicit return (which defaults to undefined) to allow other registered onMessage listeners to process the message. Returning false can prematurely close the message port for other listeners that might need to send an asynchronous response, which could break existing functionality.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new 'Tabs' feature, allowing users to create a 'space' from their open browser tabs. The implementation includes fetching tab data, extracting content via a content script, and adding a new connection. The PR also refactors UUID generation, which is a good improvement.

My review focuses on several key areas:

  • Architecture: I've pointed out an issue with using multiple onMessage listeners in background.ts, which should be consolidated for robustness.
  • Maintainability & Best Practices: In the new chromeTabs connection, I've suggested improvements regarding magic numbers, brittle CSS selectors, and string-based error handling.
  • Security & Code Quality: I've highlighted the use of innerHTML with inline event handlers, which is insecure, and suggested safer alternatives. I also noted areas where code can be simplified and type safety can be improved.

Overall, the feature is well-implemented, and these suggestions aim to improve the long-term quality and stability of the code.

Comment on lines +2 to +57
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
// Handle getTabs request for Chrome Tabs connection
if (request.action === "getTabs") {
chrome.tabs.query({}, (tabs) => {
if (chrome.runtime.lastError) {
sendResponse({ error: chrome.runtime.lastError.message });
} else {
sendResponse({ tabs: tabs });
}
});
return true;
}

// Handle getTabsWithContent request
if (request.action === "getTabsWithContent") {
chrome.tabs.query({}, async (tabs) => {
if (chrome.runtime.lastError) {
sendResponse({ error: chrome.runtime.lastError.message });
return;
}

const tabsWithContent = [];

for (const tab of tabs) {
const tabData = { ...tab, pageContent: '' }; // Add pageContent property

// Try to get page content for each tab
try {
if (tab.id && tab.url && !tab.url.startsWith('chrome://') && !tab.url.startsWith('chrome-extension://')) {
// Execute content script to get page text
const results = await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: getPageContent, // Use 'func' instead of 'function'
});

if (results && results[0] && results[0].result) {
tabData.pageContent = results[0].result;
}
}
} catch (error) {
console.error(`Could not get content for tab ${tab.id}:`, error);
// Set a fallback description
tabData.pageContent = `Content from ${tab.url ? new URL(tab.url).hostname : 'unknown site'} - unable to read page content`;
}

tabsWithContent.push(tabData);
}

sendResponse({ tabs: tabsWithContent });
});
return true;
}

// Don't interfere with other message handlers
return false;
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This file now contains multiple chrome.runtime.onMessage.addListener calls. While this can work, it's an error-prone pattern that is hard to maintain and can lead to unexpected behavior if a listener doesn't correctly return true/false. A more robust and common pattern is to use a single onMessage listener that acts as a dispatcher, routing messages to the correct handler based on request.action (e.g., using a switch statement). I recommend refactoring to consolidate all message handling into a single listener.

Comment on lines +120 to +124
allText = allText
.replace(/\s+/g, ' ') // Replace multiple whitespace with single space
.replace(/\n+/g, ' ') // Replace newlines with spaces
.replace(/\t+/g, ' ') // Replace tabs with spaces
.trim();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

These chained replace calls are redundant. The \s character class already includes spaces, newlines, and tabs. You can simplify this to a single replace call for better performance and readability.

        allText = allText.replace(/\s+/g, ' ').trim();

});

// Check if we have enough data
if (extractedData.length < 3) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The number 3 here is a magic number, making it unclear what this threshold represents without more context. It's better to extract it into a named constant (e.g., MIN_TABS_FOR_SPACE_CREATION) to improve readability and make it easier to modify in the future.

Comment on lines +89 to +105
} catch (error) {
console.error('Error in Chrome Tabs connection:', error);

const errorMessage = error.message || error.toString();
if (errorMessage.includes('Dataset too small') ||
errorMessage.includes('minimum 100 rows are required')) {
showDatasetTooSmallError(extractedData.length);
return null;
}

if (errorMessage.includes('Not enough tabs') || errorMessage.includes('No tabs found')) {
showNoTabsError();
return null;
}

throw error;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Handling errors by checking for substrings in the error message is brittle. If the error messages change, this logic will break. It's more robust to use custom error classes. For errors thrown within this file (like 'No tabs found' and 'Not enough tabs'), you can define and throw custom error classes and then check for them in the catch block using instanceof.

}

// New function for automatic retry
const createSpaceWithAutoRetry = async (extractedData: any[], establishLogSocket: establishLogSocketType, title: string, maxRetries = 5) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The extractedData parameter is typed as any[]. To improve type safety and code clarity, it's better to define a specific interface for the objects in this array. You could define an interface like this at the top of the file:

interface ExtractedTabData {
  title: string;
  semantic_title: string;
  link: string;
  snippet: string;
}

Then you can use ExtractedTabData[] as the type for extractedData here and where it's initialized in createSpace.

Comment on lines +144 to +179
const showDatasetTooSmallError = (dataCount: number) => {
const errorDiv = document.createElement('div');
errorDiv.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: linear-gradient(135deg, #ff6b6b, #ee5a52);
color: white;
padding: 20px;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
z-index: 10000;
max-width: 400px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
`;

errorDiv.innerHTML = `
<div style="display: flex; align-items: center; margin-bottom: 12px;">
<strong style="font-size: 16px;">Not Enough Data</strong>
</div>
<p style="margin: 0 0 12px 0; line-height: 1.4; font-size: 14px;">
We found ${dataCount} items, but need at least 100 to create a meaningful space.
</p>
<button onclick="this.parentElement.remove()" style="
background: rgba(255, 255, 255, 0.2);
border: 1px solid rgba(255, 255, 255, 0.3);
color: white;
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
">Got it</button>
`;

document.body.appendChild(errorDiv);
setTimeout(() => errorDiv.remove(), 8000);
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This function uses innerHTML and an inline onclick handler to create a UI element. This is not recommended due to potential security vulnerabilities (XSS) if the data were to come from an external source, and it makes the code harder to maintain. It's better to create DOM elements programmatically and attach event listeners using addEventListener. This advice also applies to the showNoTabsError function.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handled, will commit in a few

};

const injectUI = async (space_id: string, onMessage: onMessageType, registerListeners: registerListenersType) => {
const menu = document.querySelector("#hdtb-sc > div > div > div.crJ18e")?.children[0];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The query selector "#hdtb-sc > div > div > div.crJ18e" is very specific and relies on the current DOM structure and CSS classes of the Google search page. This is brittle and likely to break if Google updates their page design. If possible, try to find more stable selectors, like ones based on IDs, data-* attributes, or ARIA roles. If not, it's worth adding a comment to acknowledge this fragility. This also applies to other selectors in this file, like on line 264.

Vodiboi and others added 2 commits November 9, 2025 16:07
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant